<script setup lang="ts">
import { unwrapApiBody, type ApiResponse } from '../../../utils/http/factory'
import { buildPublicCanonicalUrl } from '../../../utils/public-canonical-url'
import {
  formatPublishedDateOnly,
  occurredOnToIsoAttr,
} from '../../../utils/timeline-datetime'

definePageMeta({
  layout: 'public',
})

const route = useRoute()
const router = useRouter()
const runtimeConfig = useRuntimeConfig()
const slug = computed(() => route.params.publicSlug as string)

function parsePage(raw: unknown): number {
  const n =
    typeof raw === 'string'
      ? Number.parseInt(raw, 10)
      : typeof raw === 'number'
        ? raw
        : Number.NaN
  if (!Number.isFinite(n) || n < 1) {
    return 1
  }
  return Math.floor(n)
}

const page = ref(parsePage(route.query.page))

watch(
  () => [route.params.publicSlug, route.query.page] as const,
  () => {
    page.value = parsePage(route.query.page)
  },
)

type PublicPostListItem = {
  title: string
  excerpt: string
  slug: string
  publishedAt: Date | null
  coverUrl?: string | null
  tags?: string[]
}
type TagMode = 'or' | 'and'

type Payload = {
  items: PublicPostListItem[]
  total: number
  page: number
  pageSize: number
  availableTags?: string[]
}

const selectedTags = ref<string[]>(
  typeof route.query.tags === 'string'
    ? route.query.tags.split(',').map(x => x.trim()).filter(Boolean)
    : [],
)
const tagMode = ref<TagMode>(route.query.tagMode === 'and' ? 'and' : 'or')
const tagModeItems = [
  { label: '任一命中 (OR)', value: 'or' },
  { label: '全部命中 (AND)', value: 'and' },
]

function onPageChange(p: number) {
  page.value = p
  const query: Record<string, string | string[] | undefined> = { ...route.query }
  if (p > 1) {
    query.page = String(p)
  }
  else {
    delete query.page
  }
  void router.replace({ query })
}

const { data, pending, error } = await useAsyncData(
  () => `public-posts-${slug.value}-${page.value}-${selectedTags.value.join(',')}-${tagMode.value}`,
  async () => {
    const base = `/api/public/profile/${encodeURIComponent(slug.value)}/posts`
    const query = new URLSearchParams()
    if (page.value > 1) {
      query.set('page', String(page.value))
    }
    if (selectedTags.value.length) {
      query.set('tags', selectedTags.value.join(','))
    }
    query.set('tagMode', tagMode.value)
    const url = query.toString() ? `${base}?${query.toString()}` : base
    const res = await $fetch<ApiResponse<Payload>>(url)
    return unwrapApiBody(res)
  },
  { watch: [slug, page, selectedTags, tagMode] },
)

const availableTags = computed(() => {
  const byApi = data.value?.availableTags ?? []
  const byItems = (data.value?.items ?? [])
    .flatMap(item => item.tags ?? [])
    .filter(Boolean)
  return [...new Set([...byApi, ...byItems])]
})

watch(
  () => [route.query.tags, route.query.tagMode] as const,
  () => {
    selectedTags.value =
      typeof route.query.tags === 'string'
        ? route.query.tags.split(',').map(x => x.trim()).filter(Boolean)
        : []
    tagMode.value = route.query.tagMode === 'and' ? 'and' : 'or'
  },
)

function updateFilterQuery(nextPage = 1) {
  const query: Record<string, string | string[] | undefined> = { ...route.query }
  if (selectedTags.value.length) {
    query.tags = selectedTags.value.join(',')
  }
  else {
    delete query.tags
  }
  query.tagMode = tagMode.value
  if (nextPage > 1) {
    query.page = String(nextPage)
  }
  else {
    delete query.page
  }
  void router.replace({ query })
}

const canonicalUrl = computed(() =>
  buildPublicCanonicalUrl(runtimeConfig.public.siteUrl, route.fullPath),
)

useHead(() => ({
  link: canonicalUrl.value
    ? [{ rel: 'canonical', href: canonicalUrl.value }]
    : [],
}))

usePageTitle(() => {
  const parts: string[] = []
  if (page.value > 1) {
    parts.push(`第 ${page.value} 页`)
  }
  parts.push('文章', `@${slug.value}`)
  return parts
})
</script>

<template>
  <UContainer class="py-8 lg:py-10 max-w-6xl">
    <div v-if="pending && !data" class="text-muted py-10">
      加载中…
    </div>
    <UAlert
      v-else-if="error && !data"
      color="error"
      title="无法加载文章列表"
      class="my-6"
    />
    <template v-else-if="data">
      <UCard class="mb-4" :ui="{ body: 'p-4 space-y-3' }">
        <div class="flex items-center justify-between gap-2">
          <div class="text-sm text-muted">
            标签筛选
          </div>
          <UButton
            size="xs"
            color="neutral"
            variant="ghost"
            @click="selectedTags = []; tagMode = 'or'; updateFilterQuery(1)"
          >
            清空
          </UButton>
        </div>
        <PostTagsInput
          :model-value="selectedTags"
          :suggestions="availableTags"
          placeholder="输入标签回车筛选"
          @update:model-value="(v) => { selectedTags = v; updateFilterQuery(1) }"
        />
        <USelect
          v-model="tagMode"
          :items="tagModeItems"
          class="w-full sm:w-56"
          @update:model-value="() => updateFilterQuery(1)"
        />
      </UCard>
      <UEmpty
        v-if="data.total === 0"
        :title="selectedTags.length ? '没有匹配结果' : '暂无公开文章'"
        :description="selectedTags.length ? '请尝试调整标签筛选条件。' : '站主尚未发布任何公开文章。'"
      />
      <template v-else>
        <h2 class="text-xs font-semibold uppercase tracking-wider text-muted mb-4">
          文章
        </h2>
        <ul class="border-t border-default">
          <li
            v-for="p in data.items"
            :key="p.slug"
            class="border-b border-default last:border-b-0"
          >
            <NuxtLink
              :to="`/@${slug}/posts/${encodeURIComponent(p.slug)}`"
              class="group flex flex-col gap-4 py-6 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-default rounded-lg sm:flex-row sm:gap-5"
            >
              <div
                v-if="p.coverUrl"
                class="w-full shrink-0 overflow-hidden rounded-xl border border-default sm:w-40"
              >
                <img
                  :src="p.coverUrl"
                  alt=""
                  class="aspect-[16/9] w-full object-cover sm:aspect-[4/3] sm:min-h-[7rem] sm:h-full"
                >
              </div>
              <div class="min-w-0 flex-1">
                <time
                  v-if="p.publishedAt"
                  class="block text-xs font-medium tabular-nums text-muted"
                  :datetime="occurredOnToIsoAttr(p.publishedAt)"
                >{{ formatPublishedDateOnly(p.publishedAt) }}</time>
                <div class="mt-1 text-xl font-semibold text-pretty text-highlighted leading-snug group-hover:text-primary transition-colors">
                  {{ p.title }}
                </div>
                <PostTagsPostTagBadges v-if="p.tags?.length" :tags="p.tags" :max="5" class="mt-2" />
                <p
                  v-if="p.excerpt"
                  class="mt-3 text-sm text-muted text-pretty leading-relaxed"
                >
                  {{ p.excerpt }}
                </p>
                <p class="mt-2 text-sm font-medium text-primary group-hover:underline">
                  查看全文
                </p>
              </div>
            </NuxtLink>
          </li>
        </ul>
        <div v-if="data.total > 10" class="flex justify-end mt-6">
          <UPagination
            :page="page"
            :total="data.total"
            items-per-page="10"
            size="sm"
            @update:page="onPageChange"
          />
        </div>
      </template>
    </template>
  </UContainer>
</template>
